﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using HalconDotNet;
using Go;

namespace fhs
{
    class ObjectShow
    {
        public string text = "";
        public HObject obj = null;
        public Color color = Color.Empty;
        public bool margin = false;
        public bool show = true;
    }

    class WindowTexts
    {
        public HTuple stringVal;
        public HTuple coordSystem;
        public HTuple row;
        public HTuple column;
        public HTuple color;
        public HTuple genParamName;
        public HTuple genParamValue;

        public static WindowTexts Make(string text, bool coordImage, int row, int col, Color color, bool box, Color boxColor)
        {
            return new WindowTexts
            {
                stringVal = text,
                coordSystem = coordImage ? "image" : "window",
                row = row,
                column = col,
                color = $"#{color.R.ToString("X2")}{color.G.ToString("X2")}{color.B.ToString("X2")}{color.A.ToString("X2")}",
                genParamName = new HTuple("box", "box_color", "shadow"),
                genParamValue = new HTuple(box ? "true" : "false", $"#{boxColor.R.ToString("X2")}{boxColor.G.ToString("X2")}{boxColor.B.ToString("X2")}{boxColor.A.ToString("X2")}", "false")
            };
        }
    }

    class CameraControl : HalconDotNet.HWindowControl
    {
        static HObject initImage = null;
        static async_timer initImageTimer = null;
        bool margin = false;
        bool highMode = false;
        Color mainColor = Color.Empty;
        HObject ho_Main = null;
        LinkedList<ObjectShow> ho_Objects = null;
        LinkedList<WindowTexts> windowTexts = new LinkedList<WindowTexts>();
        float forceWidth = 0;
        float forceHeight = 0;
        async_timer partTimer = null;
        string title = null;

        public CameraControl()
        {
            InitializeComponent();
            if (null == initImage)
            {
                // TODO
                //HOperatorSet.GenImage1(out initImage, "byte", 16 * 1024, 32 * 1024, 0);
                GlobalData.mainStrand.dispatch(delegate ()
                {
                    // TODO
                    initImageTimer = new async_timer(GlobalData.mainStrand);
                    initImageTimer.timeout(10000, delegate ()
                    {
                        //initImage.Dispose();
                        //initImage = null;
                        //initImageTimer = null;
                    });
                });
            }
            else
            {
                GlobalData.mainStrand.dispatch(() => initImageTimer?.restart());
            }
            //TODO
            //HalconWindow.DispObj(initImage);
            //HalconWindow.ClearWindow();
        }

        bool _enableZoom = true;
        public void EnableZoom(bool enableZoom = true)
        {
            _enableZoom = enableZoom;
        }

        public bool HighMode
        {
            set
            {
                if (Program.HighPartStyle)
                {
                    //TODO
                    //HOperatorSet.SetPartStyle(HalconWindow, value ? 2 : 0);
                    highMode = value;
                }
            }
        }

        public bool Zoom
        {
            get
            {
                return _enableZoom;
            }
        }

        public new double Scale
        {
            get
            {
                return _scale;
            }
        }

        public HObject MainObj
        {
            get
            {
                return ho_Main;
            }
        }

        public Color MainColor
        {
            get
            {
                return mainColor;
            }
            set
            {
                mainColor = value;
            }
        }

        new public bool Margin
        {
            get
            {
                return margin;
            }
            set
            {
                margin = value;
            }
        }

        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                title = value;
            }
        }

        public MyPoint CoordImageCoord
        {
            get
            {
                Point mouse = PointToClient(Control.MousePosition);
                HTuple width = null, height = null;
                ObjectSize(ho_Main, out width, out height);
                double imageWidth = width;
                double imageHeight = height;
                double windowRatio = (double)Width / Height;
                double imageRatio = imageWidth / imageHeight;
                if (windowRatio > imageRatio)
                {
                    return new MyPoint()
                    {
                        Row = Math.Floor(_startY * imageHeight) + mouse.Y / (Height * _scale) * imageHeight,
                        Col = Math.Floor(_startX * imageWidth + 0.5 * (imageWidth / _scale - imageWidth / (_scale * imageRatio / windowRatio))) + mouse.X / (Width * (_scale * imageRatio / windowRatio)) * imageWidth
                    };
                }
                else
                {
                    return new MyPoint()
                    {
                        Row = Math.Floor(_startY * imageHeight + 0.5 * (imageHeight / _scale - imageHeight / (_scale * windowRatio / imageRatio))) + mouse.Y / (Height * (_scale * windowRatio / imageRatio)) * imageHeight,
                        Col = Math.Floor(_startX * imageWidth) + mouse.X / (Width * _scale) * imageWidth
                    };
                }
            }
        }

        public HObject TopImage
        {
            get
            {
                HObject top = ho_Main;
                if (null != ho_Objects)
                {
                    foreach (ObjectShow obj in ho_Objects)
                    {
                        top = obj.obj;
                    }
                }
                return top;
            }
        }

        bool _isDown = false;
        bool _isMoved = false;
        bool _isSecond = false;
        double _scale = 1;
        double _startX = 0;
        double _startY = 0;
        int _downX = 0;
        int _downY = 0;
        double _downStartX = 0;
        double _downStartY = 0;
        long _downTick = 0;
        private void MouseDownEvent(object sender, MouseEventArgs e)
        {
            if (!_enableZoom)
            {
                return;
            }
            _isDown = true;
            _isMoved = false;
            _downX = e.X;
            _downY = e.Y;
            _downStartX = _startX;
            _downStartY = _startY;
            long nowTick = system_tick.get_tick_ms();
            if (nowTick - _downTick > 500)
            {
                _downTick = nowTick;
                _isSecond = false;
            }
            else
            {
                _isSecond = true;
            }
        }

        LinkedList<EventHandler> events = new LinkedList<EventHandler>();
        public new event EventHandler DoubleClick
        {
            add
            {
                events.AddLast(value);
            }
            remove
            {
                events.Remove(value);
            }
        }

        private void MouseUpEvent(object sender, MouseEventArgs e)
        {
            if (!_enableZoom)
            {
                return;
            }
            _isDown = false;
            _isMoved = false;
            long nowTick = system_tick.get_tick_ms();
            if (_isSecond)
            {
                _isSecond = false;
                if (!_isMoved && nowTick - _downTick < 500)
                {
                    EventHandler[] tev = new EventHandler[events.Count];
                    events.CopyTo(tev, 0);
                    foreach (var item in tev)
                    {
                        item.Invoke(sender, e);
                    }
                }
            }
        }

        public void ObjectSize(HObject obj, out HTuple width, out HTuple height)
        {
            if (0 != forceWidth && 0 != forceHeight)
            {
                width = forceWidth; height = forceHeight;
                return;
            }
            try
            {
                HOperatorSet.GetImageSize(obj, out width, out height);
            }
            catch (System.Exception)
            {
                try
                {
                    HTuple r1, c1;
                    HOperatorSet.SmallestRectangle1(obj, out r1, out c1, out height, out width);
                }
                catch (System.Exception)
                {
                	try
                    {
                        HTuple r1, c1;
                        HOperatorSet.SmallestRectangle1Xld(obj, out r1, out c1, out height, out width);
                    }
                	catch (System.Exception)
                    {
                        width = Width;
                        height = Height;
                    }
                }
            }
        }

        public void ShowImage(bool reShow =  false)
        {
            try
            {
                System.Threading.Monitor.Enter(HalconWindow);
                HOperatorSet.SetWindowParam(HalconWindow, "flush", "false");
                if (null != ho_Main)
                {
                    if (Program.HighPartStyle && highMode && !reShow)
                    {
                        if (null == partTimer)
                        {
                            HOperatorSet.SetPartStyle(HalconWindow, 0);
                            GlobalData.mainStrand.dispatch(delegate ()
                            {
                                if (null == partTimer)
                                {
                                    partTimer = new async_timer();
                                    partTimer.timeout(500, delegate ()
                                    {
                                        partTimer = null;
                                        if (highMode)
                                        {
                                            HOperatorSet.SetPartStyle(HalconWindow, 2);
                                            ShowImage(true);
                                        }
                                    });
                                }
                            });
                        }
                        else
                        {
                            GlobalData.mainStrand.dispatch(() => partTimer?.restart());
                        }
                    }
                    HTuple width = null, height = null;
                    ObjectSize(ho_Main, out width, out height);
                    double imageWidth = width;
                    double imageHeight = height;
                    double windowRatio = (double)Width / Height;
                    double imageRatio = imageWidth / imageHeight;
                    if (windowRatio > imageRatio)
                    {
                        ImagePart = new System.Drawing.Rectangle(
                            (int)Math.Floor(_startX * imageWidth + 0.5 * (imageWidth / _scale - imageWidth / (_scale * imageRatio / windowRatio))),
                            (int)Math.Floor(_startY * imageHeight),
                            (int)Math.Floor(imageWidth / (_scale * imageRatio / windowRatio)),
                            (int)Math.Floor(imageHeight / _scale));
                    }
                    else
                    {
                        ImagePart = new System.Drawing.Rectangle(
                            (int)Math.Floor(_startX * imageWidth),
                            (int)Math.Floor(_startY * imageHeight + 0.5 * (imageHeight / _scale - imageHeight / (_scale * windowRatio / imageRatio))),
                            (int)Math.Floor(imageWidth / _scale),
                            (int)Math.Floor(imageHeight / (_scale * windowRatio / imageRatio)));
                    }
                    HalconWindow.ClearWindow();
                    HOperatorSet.SetDraw(HalconWindow, margin ? "margin" : "fill");
                    if (Color.Empty == mainColor)
                    {
                        HOperatorSet.SetColored(HalconWindow, 6);
                    }
                    else
                    {
                        HOperatorSet.SetColor(HalconWindow, $"#{mainColor.R.ToString("X2")}{mainColor.G.ToString("X2")}{mainColor.B.ToString("X2")}{(margin ? "FF" : mainColor.A.ToString("X2"))}");
                    }
                    HalconWindow.DispObj(ho_Main);
                    if (null != ho_Objects)
                    {
                        int cnt = 0;
                        foreach (ObjectShow obj in ho_Objects)
                        {
                            if (null != obj && obj.show && !(0 == cnt++ && ho_Main == obj.obj))
                            {
                                HOperatorSet.SetDraw(HalconWindow, obj.margin ? "margin" : "fill");
                                if (Color.Empty == obj.color)
                                {
                                    HOperatorSet.SetColored(HalconWindow, 6);
                                }
                                else
                                {
                                    HOperatorSet.SetColor(HalconWindow, $"#{obj.color.R.ToString("X2")}{obj.color.G.ToString("X2")}{obj.color.B.ToString("X2")}{(obj.margin ? "FF" : obj.color.A.ToString("X2"))}");
                                }
                                HalconWindow.DispObj(obj.obj);
                            }
                        }
                    }
                }
                foreach (WindowTexts text in windowTexts)
                {
                    HOperatorSet.DispText(HalconWindow, text.stringVal, text.coordSystem, text.row, text.column, text.color, text.genParamName, text.genParamValue);
                }
                if (null != Text && 0 != Text.Length)
                {
                    HOperatorSet.DispText(HalconWindow, Text, "window", Height - 15, 0, "black", new HTuple("box", "box_color", "shadow"), new HTuple("true", "white", "false"));
                }
            }
            catch (System.Exception ec)
            {
                LogMgr.Error($"图像重绘错误 {ec.Message}");
            }
            finally
            {
                //TODO
                //HOperatorSet.FlushBuffer(HalconWindow);
                //HOperatorSet.SetWindowParam(HalconWindow, "flush", "true");
                //System.Threading.Monitor.Exit(HalconWindow);
            }
        }

        public void ShowImage(HObject ho_Image, bool refresh = true)
        {
            ho_Main = ho_Image;
            if (refresh)
            {
                ShowImage();
            }
        }

        public void ShowImage(Color color, HObject ho_Image, bool refresh = true)
        {
            ho_Main = ho_Image;
            mainColor = color;
            if (refresh)
            {
                ShowImage();
            }
        }

        public void SetObjects(IEnumerable<HObject> objects)
        {
            ho_Objects = new LinkedList<ObjectShow>();
            foreach (HObject ele in objects)
            {
                ho_Objects.AddLast(new ObjectShow { color = Color.Empty, obj = ele, margin = false });
            }
        }

        public void AddObjects(IEnumerable<HObject> objects)
        {
            if (null == ho_Objects)
            {
                ho_Objects = new LinkedList<ObjectShow>();
            }
            foreach (HObject ele in objects)
            {
                ho_Objects.AddLast(new ObjectShow { color = Color.Empty, obj = ele, margin = false });
            }
        }

        public void SetObjects(IEnumerable<ObjectShow> objects)
        {
            ho_Objects = new LinkedList<ObjectShow>();
            foreach (ObjectShow ele in objects)
            {
                ho_Objects.AddLast(ele);
            }
        }

        public void AddObjects(IEnumerable<ObjectShow> objects)
        {
            if (null == ho_Objects)
            {
                ho_Objects = new LinkedList<ObjectShow>();
            }
            foreach (ObjectShow ele in objects)
            {
                ho_Objects.AddLast(ele);
            }
        }

        public void AddObject(HObject obj)
        {
            if (null == ho_Objects)
            {
                ho_Objects = new LinkedList<ObjectShow>();
            }
            for (LinkedListNode<ObjectShow> it = ho_Objects.First; null != it ; it = it.Next)
            {
                if (it.Value.obj == obj)
                {
                    ho_Objects.Remove(it);
                    break;
                }
            }
            ho_Objects.AddLast(new ObjectShow { color = Color.Empty, obj = obj, margin = false });
        }

        public void AddObject(string text, bool margin, Color color, HObject obj)
        {
            if (null == ho_Objects)
            {
                ho_Objects = new LinkedList<ObjectShow>();
            }
            for (LinkedListNode<ObjectShow> it = ho_Objects.First; null != it; it = it.Next)
            {
                if (it.Value.obj == obj)
                {
                    ho_Objects.Remove(it);
                    break;
                }
            }
            ho_Objects.AddLast(new ObjectShow { text = text, color = color, obj = obj, margin = margin });
        }

        public void UpdateObject(HObject srcObj, bool margin, Color color, HObject obj)
        {
            if (null != ho_Objects)
            {
                for (LinkedListNode<ObjectShow> it = ho_Objects.First; null != it; it = it.Next)
                {
                    if (it.Value.obj == srcObj)
                    {
                        it.Value = new ObjectShow { text = it.Value.text, color = color, obj = obj, margin = margin };
                        return;
                    }
                }
            }
        }

        public void UpdateObject(string name, bool margin, Color color, HObject obj)
        {
            if (null != ho_Objects)
            {
                for (LinkedListNode<ObjectShow> it = ho_Objects.First; null != it; it = it.Next)
                {
                    if (it.Value.text == name)
                    {
                        it.Value = new ObjectShow { text = it.Value.text, color = color, obj = obj, margin = margin };
                        return;
                    }
                }
            }
        }

        public void SetTexts(IEnumerable<WindowTexts> texts)
        {
            windowTexts = new LinkedList<WindowTexts>();
            foreach (WindowTexts item in texts)
            {
                windowTexts.AddLast(item);
            }
        }

        public void AddTexts(IEnumerable<WindowTexts> texts)
        {
            if (null == windowTexts)
            {
                windowTexts = new LinkedList<WindowTexts>();
            }
            foreach (WindowTexts item in texts)
            {
                windowTexts.AddLast(item);
            }
        }

        public void AddText(HTuple stringVal, HTuple coordSystem, HTuple row, HTuple column, HTuple color, HTuple genParamName, HTuple genParamValue)
        {
            if (null == windowTexts)
            {
                windowTexts = new LinkedList<WindowTexts>();
            }
            windowTexts.AddLast(new WindowTexts { stringVal = stringVal, coordSystem = coordSystem, row = row, column = column, color = color, genParamName = genParamName, genParamValue = genParamValue });
        }

        public void ClearText()
        {
            windowTexts.Clear();
        }

        public void Reset()
        {
            _startX = 0;
            _startY = 0;
            _scale = 1;
            forceWidth = 0;
            forceHeight = 0;
            ho_Main = null;
            mainColor = Color.Empty;
            margin = false;
            ho_Objects = null;
            windowTexts.Clear();
            ImagePart = new Rectangle(0, 0, 640, 480);
            HalconWindow.ClearWindow();
        }

        public void Clear()
        {
            ho_Main = null;
            mainColor = Color.Empty;
            margin = false;
            ho_Objects = null;
            windowTexts.Clear();
            HalconWindow.ClearWindow();
        }

        public void ClearObj()
        {
            ho_Objects = null;
        }

        private void MouseMoveEvent(object sender, MouseEventArgs e)
        {
            if (!_enableZoom)
            {
                return;
            }
            if (_isDown)
            {
                _isMoved = true;
                if (null != ho_Main)
                {
                    HTuple width = null, height = null;
                    ObjectSize(ho_Main, out width, out height);
                    double imageWidth = width;
                    double imageHeight = height;
                    double windowRatio = (double)Width / Height;
                    double imageRatio = imageWidth / imageHeight;
                    double startX, startY;
                    System.Drawing.Rectangle imageSize = new System.Drawing.Rectangle { X = 0, Y = 0, Width = (int)imageWidth, Height = (int)imageHeight };
                    System.Drawing.Rectangle imagePart;
                    if (windowRatio > imageRatio)
                    {
                        startX = _downStartX - (double)(e.X - _downX) / (Width * (_scale * imageRatio / windowRatio));
                        startY = _downStartY - (double)(e.Y - _downY) / (Height * _scale);
                        imagePart = new System.Drawing.Rectangle(
                            (int)Math.Floor(startX * imageWidth + 0.5 * (imageWidth / _scale - imageWidth / (_scale * imageRatio / windowRatio))),
                            (int)Math.Floor(startY * imageHeight),
                            (int)Math.Floor(imageWidth / (_scale * imageRatio / windowRatio)),
                            (int)Math.Floor(imageHeight / _scale));
                    }
                    else
                    {
                        startX = _downStartX - (double)(e.X - _downX) / (Width * _scale);
                        startY = _downStartY - (double)(e.Y - _downY) / (Height * (_scale * windowRatio / imageRatio));
                        imagePart = new System.Drawing.Rectangle(
                            (int)Math.Floor(startX * imageWidth),
                            (int)Math.Floor(startY * imageHeight + 0.5 * (imageHeight / _scale - imageHeight / (_scale * windowRatio / imageRatio))),
                            (int)Math.Floor(imageWidth / _scale),
                            (int)Math.Floor(imageHeight / (_scale * windowRatio / imageRatio)));
                    }
                    if (imagePart.X + imagePart.Width >= imageSize.X &&
                        imagePart.X <= imageSize.X + imageSize.Width &&
                        imagePart.Y + imagePart.Height >= imageSize.Y &&
                        imagePart.Y <= imageSize.Y + imageSize.Height)
                    {
                        _startX = startX;
                        _startY = startY;
                    }
                    ShowImage();
                }
            }
        }

        public void SetSize(float width, float height)
        {
            forceWidth = width;
            forceHeight = height;
        }

        public SizeF GetSize()
        {
            HTuple width, height;
            ObjectSize(ho_Main, out width, out height);
            return new SizeF { Width = (float)width.D, Height = (float)height.D };
        }

        public void DrawRectangle1(out double row1, out double col1, out double row2, out double col2)
        {
            HTuple Row1, Col1, Row2, Col2;
            EnableZoom(false);
            HOperatorSet.SetColor(HalconWindow, "green");
            HOperatorSet.DrawRectangle1(HalconWindow, out Row1, out Col1, out Row2, out Col2);
            EnableZoom(true);
            row1 = (double)Row1;
            col1 = (double)Col1;
            row2 = (double)Row2;
            col2 = (double)Col2;
        }

        public void DrawRectangle2(out double row, out double col, out double phi, out double len1, out double len2)
        {
            HTuple Row, Col, Phi, Len1, Len2;
            EnableZoom(false);
            HOperatorSet.SetColor(HalconWindow, "green");
            HOperatorSet.DrawRectangle2(HalconWindow, out Row, out Col, out Phi, out Len1, out Len2);
            EnableZoom(true);
            row = Row;
            col = Col;
            phi = Phi;
            len1 = Len1;
            len2 = Len2;
        }

        public MyPoint DrawPoint()
        {
            HTuple Row, Col;
            EnableZoom(false);
            HOperatorSet.SetColor(HalconWindow, "green");
            HOperatorSet.DrawPoint(HalconWindow, out Row, out Col);
            EnableZoom(true);
            return new MyPoint { Row = Row, Col = Col };
        }

        public void DrawLine(out double row1, out double col1, out double row2, out double col2)
        {
            HTuple Row1, Col1, Row2, Col2;
            EnableZoom(false);
            HOperatorSet.SetColor(HalconWindow, "green");
            HOperatorSet.DrawLine(HalconWindow, out Row1, out Col1, out Row2, out Col2);
            EnableZoom(true);
            row1 = (double)Row1;
            col1 = (double)Col1;
            row2 = (double)Row2;
            col2 = (double)Col2;
        }

        private void HMouseWheelEvent(object sender, HMouseEventArgs e)
        {
            if (!_enableZoom)
            {
                return;
            }
            if (null != ho_Main)
            {
                double lastScale = _scale;
                double scale = _scale;
                if (e.Delta > 0)
                {
                    scale *= 11.0 / 10.0;
                    if (scale > 100)
                    {
                        scale = 100;
                    }
                }
                else
                {
                    scale *= 10.0 / 11.0;
                    if (scale < 0.1)
                    {
                        scale = 0.1;
                    }
                }
                HTuple width = null, height = null;
                ObjectSize(ho_Main, out width, out height);
                double imageWidth = width;
                double imageHeight = height;
                double windowRatio = (double)Width / Height;
                double imageRatio = imageWidth / imageHeight;
                System.Drawing.Rectangle imageSize = new System.Drawing.Rectangle { X = 0, Y = 0, Width = (int)imageWidth, Height = (int)imageHeight };
                System.Drawing.Rectangle imagePart;
                double startX = _startX + 0.5 * (1 / lastScale - 1 / scale);
                double startY = _startY + 0.5 * (1 / lastScale - 1 / scale);
                if (windowRatio > imageRatio)
                {
                    imagePart = new System.Drawing.Rectangle(
                        (int)Math.Floor(startX * imageWidth + 0.5 * (imageWidth / scale - imageWidth / (scale * imageRatio / windowRatio))),
                        (int)Math.Floor(startY * imageHeight),
                        (int)Math.Floor(imageWidth / (scale * imageRatio / windowRatio)),
                        (int)Math.Floor(imageHeight / scale));
                }
                else
                {
                    imagePart = new System.Drawing.Rectangle(
                        (int)Math.Floor(startX * imageWidth),
                        (int)Math.Floor(startY * imageHeight + 0.5 * (imageHeight / scale - imageHeight / (scale * windowRatio / imageRatio))),
                        (int)Math.Floor(imageWidth / scale),
                        (int)Math.Floor(imageHeight / (scale * windowRatio / imageRatio)));
                }
                if (imagePart.X + imagePart.Width >= imageSize.X &&
                    imagePart.X <= imageSize.X + imageSize.Width &&
                    imagePart.Y + imagePart.Height >= imageSize.Y &&
                    imagePart.Y <= imageSize.Y + imageSize.Height)
                {
                    _startX = startX;
                    _startY = startY;
                    _scale = scale;
                }
            }
            ShowImage();
        }

        private void CameraControl_Paint(object sender, PaintEventArgs e)
        {
            ShowImage();
        }

        private void CameraControl_SizeChanged(object sender, EventArgs e)
        {
            ShowImage();
        }

        private void InitializeComponent()
        {
            SuspendLayout();
            // 
            // CameraControl
            // 
            Name = "CameraControl";
            Size = new System.Drawing.Size(1133, 906);
            WindowSize = new System.Drawing.Size(1133, 906);
            HMouseWheel += new HalconDotNet.HMouseEventHandler(HMouseWheelEvent);
            Paint += new System.Windows.Forms.PaintEventHandler(CameraControl_Paint);
            SizeChanged += new System.EventHandler(CameraControl_SizeChanged);
            MouseDown += new System.Windows.Forms.MouseEventHandler(MouseDownEvent);
            MouseMove += new System.Windows.Forms.MouseEventHandler(MouseMoveEvent);
            MouseUp += new System.Windows.Forms.MouseEventHandler(MouseUpEvent);
            ResumeLayout(false);
        }
    }
}
